package cn.iocoder.yudao.module.system.service.permission;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import com.google.common.collect.Sets;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;

/**
 * PermissionCacheService for user permission caching
 *
 * @author Jarvis
 */
@Service
@RequiredArgsConstructor
public class PermissionCacheService {

    private final PermissionService permissionService;
    private final RoleService roleService;
    private final MenuService menuService;
    private final AdminUserService adminUserService;

    // Cache names
    public static final String USER_PERMISSION_CACHE = "user:permission:";
    public static final String USER_ROLE_CACHE = "user:role:";
    public static final String ROLE_MENU_CACHE = "role:menu:";
    public static final String MENU_DETAIL_CACHE = "menu:detail:";

    /**
     * 获取用户权限信息 getUserPermissionInfo
     */
    @Cacheable(value = USER_PERMISSION_CACHE, key = "#userId")
    public UserPermissionInfo getUserPermissionInfo(Long userId) {
        // 1. Get user info
        AdminUserDO user = adminUserService.getUser(userId);
        if (user == null) {
            return new UserPermissionInfo();
        }

        // 2. Get user roles
        Set<Long> roleIds = getUserRoleIds(userId);
        if (CollUtil.isEmpty(roleIds)) {
            return new UserPermissionInfo(user, Collections.emptyList(), Collections.emptyList());
        }

        // 3. Get role info
        List<RoleDO> roles = getRoles(roleIds);

        // 4. Get menu info
        Set<Long> menuIds = getRoleMenuIds(roleIds);
        List<MenuDO> menus = getMenus(menuIds);

        return new UserPermissionInfo(user, roles, menus);
    }

    /**
     * 获取用户角色ID列表 getUserRoleIds
     */
    @Cacheable(value = USER_ROLE_CACHE, key = "#userId")
    public Set<Long> getUserRoleIds(Long userId) {
        return permissionService.getUserRoleIdListByUserId(userId);
    }

    /**
     * 获取角色菜单ID列表 getRoleMenuIds
     */
    @Cacheable(value = ROLE_MENU_CACHE, key = "#roleId")
    public Set<Long> getRoleMenuIds(Long roleId) {
        return permissionService.getRoleMenuListByRoleId(roleId);
    }

    /**
     * 批量获取角色菜单ID列表
     */
    public Set<Long> getRoleMenuIds(Set<Long> roleIds) {
        if (CollUtil.isEmpty(roleIds)) {
            return Collections.emptySet();
        }
        return roleIds.stream()
                .flatMap(roleId -> getRoleMenuIds(roleId).stream())
                .collect(Collectors.toSet());
    }

    /**
     * 获取菜单详情 getMenu
     */
    @Cacheable(value = MENU_DETAIL_CACHE, key = "#menuId")
    public MenuDO getMenu(Long menuId) {
        return menuService.getMenu(menuId);
    }

    /**
     * 获取角色列表 getRoles
     */
    public List<RoleDO> getRoles(Set<Long> roleIds) {
        if (CollUtil.isEmpty(roleIds)) {
            return Collections.emptyList();
        }
        List<RoleDO> roles = roleService.getRoleList(roleIds);
        return roles.stream()
                .filter(role -> CommonStatusEnum.ENABLE.getStatus().equals(role.getStatus()))
                .collect(Collectors.toList());
    }

    /**
     * 获取菜单列表 getMenus
     */
    public List<MenuDO> getMenus(Set<Long> menuIds) {
        if (CollUtil.isEmpty(menuIds)) {
            return Collections.emptyList();
        }
        List<MenuDO> menus = menuService.getMenuList(menuIds);
        return menuService.filterDisableMenus(menus);
    }

    /**
     * 清除用户权限缓存 clearUserPermissionCache
     */
    @CacheEvict(value = USER_PERMISSION_CACHE, key = "#userId")
    public void clearUserPermissionCache(Long userId) {
        // Clear user permission cache
    }

    /**
     * 清除用户角色缓存 clearUserRoleCache
     */
    @CacheEvict(value = USER_ROLE_CACHE, key = "#userId")
    public void clearUserRoleCache(Long userId) {
        // Clear user role cache
    }

    /**
     * 清除角色菜单缓存 clearRoleMenuCache
     */
    @CacheEvict(value = ROLE_MENU_CACHE, key = "#roleId")
    public void clearRoleMenuCache(Long roleId) {
        // Clear role menu cache
    }

    /**
     * 清除菜单详情缓存 clearMenuCache
     */
    @CacheEvict(value = MENU_DETAIL_CACHE, key = "#menuId")
    public void clearMenuCache(Long menuId) {
        // Clear menu detail cache
    }

    /**
     * 清除所有用户权限缓存 clearAllUserPermissionCache
     */
    @CacheEvict(value = USER_PERMISSION_CACHE, allEntries = true)
    public void clearAllUserPermissionCache() {
        // Clear all user permission cache
    }

    /**
     * 清除所有角色菜单缓存 clearAllRoleMenuCache
     */
    @CacheEvict(value = ROLE_MENU_CACHE, allEntries = true)
    public void clearAllRoleMenuCache() {
        // Clear all role menu cache
    }

    /**
     * 清除所有菜单详情缓存 clearAllMenuCache
     */
    @CacheEvict(value = MENU_DETAIL_CACHE, allEntries = true)
    public void clearAllMenuCache() {
        // Clear all menu detail cache
    }

    /**
     * User permission info wrapper class
     */
    public static class UserPermissionInfo {
        private AdminUserDO user;
        private List<RoleDO> roles;
        private List<MenuDO> menus;

        public UserPermissionInfo() {
        }

        public UserPermissionInfo(AdminUserDO user, List<RoleDO> roles, List<MenuDO> menus) {
            this.user = user;
            this.roles = roles;
            this.menus = menus;
        }

        public AdminUserDO getUser() {
            return user;
        }

        public List<RoleDO> getRoles() {
            return roles;
        }

        public List<MenuDO> getMenus() {
            return menus;
        }
    }
}